/**
 * @Project: DataWiki Platform
 * @Package: com.skieer.datawiki.util
 * @Description: TODO
 * @author tony.he@skieer.com
 * @version V1.0.0
 * @date 2015年3月31日 下午5:22:04
 * @copyright Copyright(c) 2013-2015深圳视界信息技术有限公司版权所有
 */
package com.rr.util;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonParser.Feature;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

import static java.util.Collections.emptyList;
import static java.util.Objects.isNull;
import static java.util.Objects.nonNull;
import static org.apache.commons.lang3.ObjectUtils.isEmpty;

public class JsonUtils {

    private static final Logger log = LoggerFactory.getLogger(JsonUtils.class);

    private static final ObjectMapper BASE_OBJECT_MAPPER;
    private static final ObjectMapper EXTENDED_OBJECT_MAPPER;
    private static final ObjectMapper BEAN_OBJECT_MAPPER;
    private static final ObjectMapper FASTJSON_MOCK_OBJECT_MAPPER;

    /**
     * json空对象字符串
     */
    public static final String EMPTY_OBJECT = "{}";
    /**
     * json空集合字符串
     */
    public static final String EMPTY_ARRAY = "[]";

    static {
        List<Module> modules = ObjectMapper.findModules();

        BASE_OBJECT_MAPPER = new ObjectMapper()
                .disable(SerializationFeature.FAIL_ON_EMPTY_BEANS)
                .registerModules(modules)
                // 时间Date按照时间戳格式序列化
                .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        EXTENDED_OBJECT_MAPPER = new ObjectMapper()
                .disable(SerializationFeature.FAIL_ON_EMPTY_BEANS)
                .registerModules(modules)
                // 时间Date按照时间戳格式序列化
                .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
                .enable(SerializationFeature.WRAP_ROOT_VALUE);
        BEAN_OBJECT_MAPPER = new ObjectMapper()
                .registerModules(modules)
                .enable(Feature.ALLOW_UNQUOTED_FIELD_NAMES)
                .enable(Feature.ALLOW_SINGLE_QUOTES)
                .disable(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES)
                .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
        FASTJSON_MOCK_OBJECT_MAPPER = new ObjectMapper()
                .registerModules(modules)
                // 如果json中有新增的字段并且是实体类类中不存在的，不报错
                .configure(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES, true)
                // 如果存在未知属性，则忽略不报错
                .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
                // "" => null
                .configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true)
                .configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false)
                // 时间Date按照时间戳格式序列化
                .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
                // 允许key没有双引号
                .configure(Feature.ALLOW_UNQUOTED_FIELD_NAMES, true)
                // 允许key有单引号
                .configure(Feature.ALLOW_SINGLE_QUOTES, true)
                // 允许整数以0开头
                .configure(Feature.ALLOW_NUMERIC_LEADING_ZEROS, true)
                // 允许字符串中存在回车换行控制符
                .configure(Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true)
                // 忽略字段值是null的字段
                .setSerializationInclusion(JsonInclude.Include.NON_NULL);
    }

    private JsonUtils() {
    }

    /**
     * 序列化
     *
     * @param obj 待序列化的对象
     * @return JSON 字符串
     */
    public static String toJson(Object obj) {
        return toJson(FASTJSON_MOCK_OBJECT_MAPPER, obj, null);
    }

    /**
     * 反序列化
     *
     * @param json          JSON 字符串
     * @param typeReference 类型
     * @param <T>           对象泛型
     * @return Java 对象
     */
    public static <T> T fromJson(String json, TypeReference<T> typeReference) {
        if(json == null){
            return  null;
        }
        try {
            return FASTJSON_MOCK_OBJECT_MAPPER.readValue(json, typeReference);
        } catch (Exception e) {
            log.error("JsonUtils 反序列化错误", e);
            throw new RuntimeException("JsonUtils 反序列化错误");
        }
    }

    /**
     * 反序列化
     *
     * @param json  JSON 字符串
     * @param clazz 类型
     * @param <T>   对象泛型
     * @return Java 对象
     */
    public static <T> T fromJson(String json, Class<T> clazz) {
        if(json == null){
            return  null;
        }
        try {
            return FASTJSON_MOCK_OBJECT_MAPPER.readValue(json, clazz);
        } catch (Exception e) {
            log.error("JsonUtils 反序列化错误", e);
            throw new RuntimeException("JsonUtils 反序列化错误");
        }
    }

    /**
     * 如果要偷懒类似实现之前 FastJson 的 {@code JSON.parseObject} 或者 {@code JSON.parseArray} 功能，可以用这个。
     * 尽量去声明一个实体，利于维护。
     * 具体用法参考 {@link JsonNode}
     *
     * @param json JSON 字符串
     * @return JSON 节点
     */
    public static JsonNode fromJson(String json) {
        if(json == null){
            return  null;
        }
        Objects.requireNonNull(json, "JsonUtils 待反序列化的字符串为 null");
        try {
            return FASTJSON_MOCK_OBJECT_MAPPER.readTree(json);
        } catch (Exception e) {
            log.error("JsonUtils 反序列化错误", e);
            throw new RuntimeException("JsonUtils 反序列化错误");
        }
    }

     /**
     * 创建一个 {@link ObjectNode}，可以用来自己构造 JSON 对象
     *
     * @return {@link ObjectNode}
     */
    public static ObjectNode createObjectNode() {
        return FASTJSON_MOCK_OBJECT_MAPPER.createObjectNode();
    }

    /**
     * 创建一个 {@link ArrayNode}，可以用来自己构造 JSON 数组
     *
     * @return {@link ArrayNode}
     */
    public static ArrayNode createArrayNode() {
        return FASTJSON_MOCK_OBJECT_MAPPER.createArrayNode();
    }

    /**
     * 将对象序列化成 JSON
     *
     * @param objectMapper 序列化器
     * @param obj          待序列化的对象
     * @param defaultJson  对象为 {@code null} 或者 系列化出错时的返回值。为 {@code null} 则表示抛出异常
     * @return JSON 字符串
     */
    public static String toJson(ObjectMapper objectMapper, Object obj, String defaultJson) {
        if (obj == null) {
            if (defaultJson == null) {
                throw new NullPointerException("JsonUtils 待序列化的对象为 null");
            } else {
                return defaultJson;
            }
        }
        try {
            return objectMapper.writeValueAsString(obj);
        } catch (JsonProcessingException e) {
            if (defaultJson == null) {
                throw new RuntimeException("JsonUtils 反序列化错误");
            } else {
                log.error("JsonUtils 序列化错误", e);
                return defaultJson;
            }
        }
    }

    // XXX 不知道为啥要要这么多种，怕有些地方根据返回值判断作为逻辑，兼容重载
    @Deprecated
    public static String toString(Object obj) {
        return toJson(BASE_OBJECT_MAPPER, obj, "null");
    }

    // XXX 不知道为啥要要这么多种，怕有些地方根据返回值判断作为逻辑，兼容重载
    @Deprecated
    public static String obj2Json(Object obj) {
        return toJson(BASE_OBJECT_MAPPER, obj, "");
    }

    // XXX 不知道为啥要要这么多种，怕有些地方根据返回值判断作为逻辑，兼容重载
    @Deprecated
    public static String convert2json(Object obj) {
        return toJson(EXTENDED_OBJECT_MAPPER, obj, "");
    }

    @Deprecated
    public static <T> T json2Bean(String bean, TypeReference<T> typeReference) {
        try {
            return BEAN_OBJECT_MAPPER.readValue(bean, typeReference);
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }

    @Deprecated
    public static <T> T json2Bean(String bean, Class<T> valueType) {
        try {
            return BEAN_OBJECT_MAPPER.readValue(bean, valueType);
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }

    @Deprecated
    public static <T> T json2Bean(String scr, JavaType javaType) {
        try {
            return BEAN_OBJECT_MAPPER.readValue(scr, javaType);
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }

    @Deprecated
    public static JavaType parametricType(Class<?> parametrized, Class<?>... parameterClasses) {
        return BASE_OBJECT_MAPPER.getTypeFactory().constructParametricType(parametrized, parameterClasses);
    }

    @Deprecated
    public static void dump(Object obj) {
        log.debug("\n\n {} \n\n", obj2Json(obj));
    }

    /**
     * 返回指定属性或路径的值，并转换成 String 集合类型
     *
     * @param jsonNode json节点对象
     * @param str      属性或路径
     * @return 属性或路径的 String 值
     */
    public static List<String> asList(JsonNode jsonNode, String str) {
        return asList(jsonNode, str, emptyList());
    }

    /**
     * 返回指定属性或路径的值，并转换成 String 集合类型
     *
     * @param jsonNode json节点对象
     * @param str      属性或路径
     * @return 属性或路径的 String 值
     */
    public static List<String> asList(JsonNode jsonNode, String str, List<String> dft) {
        ArrayNode arrayNode = isPath(str) ? (ArrayNode) jsonNode.at(str) : (ArrayNode) jsonNode.get(str);
        if (isEmpty(arrayNode)) {
            return dft;
        }
        List<String> result = new ArrayList<>();
        for (JsonNode node : arrayNode) {
            result.add(node.asText());
        }
        return result;
    }

    /**
     * 返回指定属性或路径的值，并转换成 String 集合类型
     *
     * @param jsonNode json节点对象
     * @param str      属性或路径
     * @return 属性或路径的 String 值
     */
    public static String asListStr(JsonNode jsonNode, String str, String split) {
        return String.join(split, asList(jsonNode, str, emptyList()));
    }

    /**
     * 返回指定属性或路径的值，并转换成 String 集合类型
     *
     * @param jsonNode json节点对象
     * @param str      属性或路径
     * @return 属性或路径的 String 值
     */
    public static String asListStr(JsonNode jsonNode, String str, List<String> dft, String split) {
        return String.join(split, asList(jsonNode, str, dft));
    }

    /**
     * 返回指定属性或路径的值，并转换成 String 类型
     *
     * @param jsonNode json节点对象
     * @param str      属性或路径
     * @return 属性或路径的 String 值
     */
    public static String asText(JsonNode jsonNode, String str) {
        return asText(jsonNode, str, null);
    }

    /**
     * 返回指定属性或路径的值，并转换成 String 类型
     *
     * @param jsonNode json节点对象
     * @param str      属性或路径
     * @param dft      默认值
     * @return 属性或路径的 String 值，不存在使用默认值
     */
    public static String asText(JsonNode jsonNode, String str, String dft) {
        JsonNode node = isPath(str) ? jsonNode.at(str) : jsonNode.get(str);
        return nonNull(node) ? node.asText(dft) : dft;
    }

    /**
     * 返回指定属性或路径的值，并转换成 String 类型,
     *
     * @param jsonNode json节点对象
     * @param ps       属性集合
     * @return 属性或路径的 String 值，不存在使用默认值
     */
    public static String asTextPriority(JsonNode jsonNode, String... ps) {
        if (isNull(ps)) {
            return null;
        }
        for (String p : ps) {
            JsonNode node = isPath(p) ? jsonNode.at(p) : jsonNode.get(p);
            if (nonNull(node) && StringUtils.length(node.asText()) > 0) {
                return node.asText();
            }
        }
        return null;
    }

    /**
     * 返回指定属性或路径的值，并转换成int类型
     *
     * @param jsonNode json节点对象
     * @param str      属性或路径
     * @return 属性或路径的int值
     */
    public static int asInt(JsonNode jsonNode, String str) {
        return asInt(jsonNode, str, 0);
    }

    /**
     * 返回指定属性或路径的值，并转换成int类型
     *
     * @param jsonNode json节点对象
     * @param str      属性或路径
     * @param dft      默认值
     * @return 属性或路径的int值，不存在使用默认值
     */
    public static int asInt(JsonNode jsonNode, String str, int dft) {
        JsonNode node = isPath(str) ? jsonNode.at(str) : jsonNode.get(str);
        return nonNull(node) ? node.asInt(dft) : dft;
    }

    /**
     * 返回指定属性或路径的值，并转换成 long 类型
     *
     * @param jsonNode json节点对象
     * @param str      属性或路径
     * @return 属性或路径的 long 值
     */
    public static long asLong(JsonNode jsonNode, String str) {
        return asLong(jsonNode, str, 0);
    }

    /**
     * 返回指定属性或路径的值，并转换成 long 类型
     *
     * @param jsonNode json节点对象
     * @param str      属性或路径
     * @param dft      默认值
     * @return 属性或路径的 long 值，不存在使用默认值
     */
    public static long asLong(JsonNode jsonNode, String str, long dft) {
        JsonNode node = isPath(str) ? jsonNode.at(str) : jsonNode.get(str);
        return nonNull(node) ? node.asLong(dft) : dft;
    }

    /**
     * 返回指定属性或路径的值，并转换成 double 类型
     *
     * @param jsonNode json节点对象
     * @param str      属性或路径
     * @return 属性或路径的 double 值
     */
    public static double asDouble(JsonNode jsonNode, String str) {
        return asDouble(jsonNode, str, 0.0);
    }

    /**
     * 返回指定属性或路径的值，并转换成 double 类型
     *
     * @param jsonNode json节点对象
     * @param str      属性或路径
     * @param dft      默认值
     * @return 属性或路径的 double 值，不存在使用默认值
     */
    public static double asDouble(JsonNode jsonNode, String str, double dft) {
        JsonNode node = isPath(str) ? jsonNode.at(str) : jsonNode.get(str);
        return nonNull(node) ? node.asDouble(dft) : dft;
    }

    /**
     * 返回指定属性或路径的值，并转换成 boolean 类型
     *
     * @param jsonNode json节点对象
     * @param str      属性或路径
     * @return 属性或路径的 boolean 值
     */
    public static boolean asBoolean(JsonNode jsonNode, String str) {
        return asBoolean(jsonNode, str, false);
    }

    /**
     * 返回指定属性或路径的值，并转换成 boolean 类型
     *
     * @param jsonNode json节点对象
     * @param str      属性或路径
     * @param dft      默认值
     * @return 属性或路径的 boolean 值，不存在使用默认值
     */
    public static boolean asBoolean(JsonNode jsonNode, String str, boolean dft) {
        JsonNode node = isPath(str) ? jsonNode.at(str) : jsonNode.get(str);
        return nonNull(node) ? node.asBoolean(dft) : dft;
    }

    /**
     * 是否是路径
     *
     * @param str 字符串，/开头是路径，否则就是属性
     * @return 是否是路径
     */
    private static boolean isPath(String str) {
        return StringUtils.isNotBlank(str) && str.startsWith("/");
    }

}
